import {PublicInstanceProxyHandlers} from "./PublicInstanceProxyHandlers";
import {initProps} from "./ComponentProps";
import {shallowReadonly} from "../reactivity/reactive";
import {emit} from "./ComponentEmit";
import {initSlots} from "./CompoentSlots";
import {proxyRefs} from "../reactivity";


let currentInstance = null;

/**
 * 创建组件实例
 * @param vnode
 */
export function createComponentInstance(vnode, parent) {
    // 给组件实例添加上slots 属性.
    const component = {
        vnode,
        type: vnode.type,
        setupState: {},
        props: {},
        slots: {},
        provides: parent ? parent.provides : {},
        parent,
        isMounted: false,
        subTree: {},
        emit: () => {
        }
    };
    //  给emit函数传入一个默认的参数,当前组件实例,为了获得 props属性
    // 可以使用bind 函数来做一些操作，将定义好的组件实例传递给emit函数。
    component.emit = emit.bind(null, component) as any;
    return component;
}

//  初始化组件
export function setupComponent(instance) {
//   初始化属性 initProps
    initProps(instance, instance.vnode.props);
//    初始化插槽 initSlots,就是把虚拟节点的children赋值给slots
    initSlots(instance, instance.vnode.children);
    //  初始化有状态的组件
    setupStatefulComponent(instance)
}

// 初始化有状态的组件
function setupStatefulComponent(instance) {
    const Component = instance.type;
    //  这个空对象一般被称为ctx,但是这个地方我不太理解
    // {_: instance} 是一个对象声明,_是对象的key,instance是对象的值
    instance.proxy = new Proxy({_: instance}, PublicInstanceProxyHandlers)
    // 从有状态的组件中取出setup函数.
    const {setup} = Component;
    //  如果setup 函数存在则执行,因为用户可能没写setup,所以此处判断一下.
    if (setup) {
        setcCurrentInstance(instance);
        //  对于 props 的处理是只把第一层编程响应式的
        // 把虚拟节点的emit函数,赋给组件实例的emit函数,通过setup的第二个参数传递给子组件
        const setupResult = setup(shallowReadonly(instance.props), {
            emit: instance.emit
        });
        setcCurrentInstance(null);
        // 处理初始化后的结果
        handleSetupResult(instance, setupResult);
    }

//     初始化的时候创建一个代理对象.调用render函数的时候,把这个代理对象绑定到render的this上


}

function handleSetupResult(instance, setupResult: any) {
    //  如何组件的setup函数返回了对象,那么就把返回的对象赋值给虚拟节点的setupState属性.
    if (typeof setupResult === "object") {
        // 使用proxyRefs包裹一下，这样 html中使用响应式变量的时候就不用带.value了。
        instance.setupState = proxyRefs(setupResult);
    }
    //  结束组件初始化
    finishComponentSetup(instance)
}

/**
 * 结束组件初始化
 * @param instance
 */
function finishComponentSetup(instance) {
    //
    const Component = instance.type;
    // 将组件的 render赋值给虚拟节点的render,结束组件的创建过程
    instance.render = Component.render;
}

export function setcCurrentInstance(instance) {
    currentInstance = instance;
}

export function getCurrentInstance() {
    return currentInstance;
}

